﻿using Dapper;
using SenGe.Domain;
using System.Data;
using System.Reflection;
using System.Text;

namespace SenGe.Infrastructure
{
    /// <summary>
    /// 基础仓储服务
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BaseRepository<T> : IBaseRepository<T> where T : BaseLongIdEntity
    {
        /// <summary>
        /// 数据库连接对象
        /// </summary>
        private readonly IDbConnection _db;
        /// <summary>
        /// 构造函数传入依赖注入对象
        /// </summary>
        /// <param name="db">db</param>
        public BaseRepository(IDbConnection db)
        {
            _db = db;
        }

        /// <summary>
        /// 检查数据库是否链接正确
        /// </summary>
        /// <returns></returns>
        public async Task<bool> CheckDb()
        {
            try
            {
                var sql = @"SELECT `User` FROM mysql.`user` LIMIT 0,1;";
                string? username = await _db.ExecuteScalarAsync<string>(sql);
                return username?.Length > 0;
            }
            catch { }
            return false;
        }
        /// <summary>
        /// 插入数据
        /// </summary>
        /// <param name="entity">数据模型</param>
        /// <returns></returns>
        public bool Insert(T entity)
        {
            if (entity == null) return true;
            string sql = GenerateInsertSql();
            return _db.Execute(sql, entity) == 1;
        }
        /// <summary>
        /// 插入数据
        /// </summary>
        /// <param name="entity">数据模型</param>
        /// <returns></returns>
        public async Task<bool> InsertAsync(T entity)
        {
            if (entity == null) return true;
            string sql = GenerateInsertSql();
            return await _db.ExecuteAsync(sql, entity) == 1;
        }
        /// <summary>
        /// 插入数据列表
        /// </summary>
        /// <param name="entities">数据模型列表</param>
        /// <returns></returns>
        public bool InsertList(List<T> entities)
        {
            if (entities == null || entities.Count == 0) return true;
            string sql = GenerateInsertSql();
            return _db.Execute(sql, entities) == entities.Count;
        }
        /// <summary>
        /// 插入数据列表
        /// </summary>
        /// <param name="entities">数据模型列表</param>
        /// <returns></returns>
        public async Task<bool> InsertListAsync(List<T> entities)
        {
            if (entities == null || entities.Count == 0) return true;
            string sql = GenerateInsertSql();
            return await _db.ExecuteAsync(sql, entities) == entities.Count;
        }
        /// <summary>
        /// 通过编号查询数据对象
        /// </summary>
        /// <param name="id">数据编号</param>
        /// <returns></returns>
        public T? GetById(object id)
        {
            var key = GetTableKeyName();
            string sql = string.Format("SELECT * FROM {0} WHERE {1}=@{1};", GetTableName(), key);
            var parameters = new DynamicParameters();
            parameters.Add("@" + key, id);
            return _db.QueryFirstOrDefault<T>(sql, parameters);
        }
        /// <summary>
        /// 通过编号查询数据对象
        /// </summary>
        /// <param name="id">数据编号</param>
        /// <returns></returns>
        public async Task<T?> GetByIdAsync(object id)
        {
            var key = GetTableKeyName();
            string sql = string.Format("SELECT * FROM {0} WHERE {1}=@{1};", GetTableName(), key);
            var parameters = new DynamicParameters();
            parameters.Add("@" + key, id);
            return await _db.QueryFirstOrDefaultAsync<T>(sql, parameters);
        }
        /// <summary>
        /// 获取分页数据
        /// </summary>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页大小</param>
        /// <returns></returns>
        public PagedList<T> GetPagedList(int pageIndex = 1, int pageSize = 20)
        {
            PagedList<T> result = new PagedList<T>();
            result.PageIndex = pageIndex;
            result.PageSize = pageSize;
            var sql = string.Format("SELECT COUNT(1) FROM {0};", GetTableName());
            result.TotalCount = _db.ExecuteScalar<int>(sql);
            sql = string.Format("SELECT * FROM {0} ORDER BY AddTime DESC LIMIT {1},{2};", GetTableName(), (pageIndex - 1) * pageSize, pageSize);
            result.List = _db.Query<T>(sql);
            return result;
        }
        /// <summary>
        /// 获取分页数据
        /// </summary>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页大小</param>
        /// <returns></returns>
        public async Task<PagedList<T>> GetPagedListAsync(int pageIndex = 1, int pageSize = 20)
        {
            PagedList<T> result = new PagedList<T>();
            result.PageIndex = pageIndex;
            result.PageSize = pageSize;
            var sql = string.Format("SELECT COUNT(1) FROM {0};", GetTableName());
            result.TotalCount = await _db.ExecuteScalarAsync<int>(sql);
            sql = string.Format("SELECT * FROM {0} ORDER BY AddTime DESC LIMIT {1},{2};", GetTableName(), (pageIndex - 1) * pageSize, pageSize);
            result.List = await _db.QueryAsync<T>(sql);
            return result;
        }
        /// 更新数据
        /// </summary>
        /// <param name="entity">数据模型</param>
        /// <returns></returns>
        public bool Update(T entity)
        {
            if (entity == null) return true;
            var sql = GenerateUpdateSql();
            return _db.Execute(sql, entity) == 1;
        }
        /// <summary>
        /// 更新数据
        /// </summary>
        /// <param name="entity">数据模型</param>
        /// <returns></returns>
        public async Task<bool> UpdateAsync(T entity)
        {
            if (entity == null) return true;
            var sql = GenerateUpdateSql();
            return await _db.ExecuteAsync(sql, entity) == 1;
        }
        /// <summary>
        /// 删除指定编号数据模型
        /// </summary>
        /// <param name="id">编号</param>
        /// <returns></returns>
        public bool Delete(object id)
        {
            var key = GetTableKeyName();
            string sql = string.Format("DELETE FROM {0} WHERE {1}=@{1};", GetTableName(), key);
            var parameters = new DynamicParameters();
            parameters.Add("@" + key, id);
            return _db.Execute(sql, parameters) == 1;
        }
        /// <summary>
        /// 删除指定编号数据模型
        /// </summary>
        /// <param name="id">编号</param>
        /// <returns></returns>
        public async Task<bool> DeleteAsync(object id)
        {
            var key = GetTableKeyName();
            string sql = string.Format("DELETE FROM {0} WHERE {1}=@{1};", GetTableName(), key);
            var parameters = new DynamicParameters();
            parameters.Add("@" + key, id);
            return await _db.ExecuteAsync(sql, parameters) == 1;
        }

        #region 生成sql
        /// <summary>
        /// 生成插入SQL语句
        /// </summary>
        /// <returns></returns>
        private static string GenerateInsertSql()
        {
            var propertys = CheckAllPropertyList();
            string sql = string.Format("Insert Into {0}({1}) Values({2});",
                GetTableName(),
                string.Join(",", propertys.Keys),
                string.Join(",", propertys.Values));
            return sql;
        }
        /// <summary>
        /// 生成插入SQL语句
        /// </summary>
        /// <returns></returns>
        private static string GenerateUpdateSql()
        {
            var propertys = CheckAllPropertyList();
            StringBuilder sb = new StringBuilder();
            string key = GetTableKeyName();
            foreach (var item in propertys)
            {
                if (item.Key.Equals(key)) continue;
                sb.AppendFormat("{0}={1},", item.Key, item.Value);
            }
            string itemsql = sb.ToString();
            string sql = string.Format("UPDATE {0} SET {1} WHERE {2}=@{2}",
                GetTableName(),
                itemsql.Substring(0, itemsql.Length - 1),
                key);
            return sql;
        }
        #endregion

        #region 通用
        /// <summary>
        /// 检查获取对象的所有属性列表
        /// </summary>
        /// <returns></returns>
        private static Dictionary<string, string> CheckAllPropertyList()
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            PropertyInfo[] props = typeof(T).GetProperties();
            object[]? attributes = null;
            foreach (var property in props)
            {
                attributes = property.GetCustomAttributes(typeof(SenIgnoreAttribute), false);
                if (attributes.Any()) continue;
                result.Add(property.Name, "@" + property.Name);
            }
            return result;
        }

        /// <summary>
        /// 获取对应数据库表名称
        /// 未配置时默认返回泛型对象名
        /// </summary>
        /// <returns></returns>
        public static string GetTableName()
        {
            SenTableNameAttribute[] attributes = (SenTableNameAttribute[])typeof(T).GetCustomAttributes(typeof(SenTableNameAttribute), false);
            if (attributes.Any())
            {
                return attributes[0].Name;
            }
            return typeof(T).Name;
        }
        /// <summary>
        /// 获取对应数据库表主键名称
        /// 未配置时默认返回【Id】
        /// </summary>
        /// <returns></returns>
        private static string GetTableKeyName()
        {
            List<string> keys = new List<string>();
            PropertyInfo[] props = typeof(T).GetProperties();
            object[]? attributes = null;
            foreach (var property in props)
            {
                attributes = property.GetCustomAttributes(typeof(SenKeyAttribute), false);
                if (attributes.Any())
                {
                    keys.Add(property.Name);
                }
            }
            return keys.Count == 1 ? keys[0] : "Id";
        }
        #endregion
    }
}
